package physics.globalforce;

import physics.Assembly;
import physics.EnvironmentSingleton.EnvironmentalForce;
import physics.Force;
import physics.component.Mass;


/**
 * A class that models forces conforming to:
 * x*magnitude/(distance)^exponent.
 *
 * @author Kevin Gao, Connor Gordon
 *
 */
public abstract class ExponentialForce extends GlobalForce {

    private double myMagnitude = 0;
    private double myExponent = 0;

    /**
     * Constructor to enforce that environmental force should be provided.
     *
     * @param ef Type of force
     */
    public ExponentialForce (final EnvironmentalForce ef) {
        super(ef);
    }

    /**
     * Constructor that sets type, magnitude, and exponent.
     *
     * @param ef Type of force
     * @param magnitude Magnitude of force
     * @param exponent Inverse exponent for force
     */
    public ExponentialForce (final EnvironmentalForce ef,
            final double magnitude, final double exponent) {
        super(ef);
        setForceMagnitudeAndExponent(magnitude, exponent);
    }

    /**
     * Setter for force components.
     *
     * @param magnitude Desired magnitude of force
     * @param exponent Desired exponent, where magnitude/(distance)^exponent is
     *        the used magnitude
     */
    public final void setForceMagnitudeAndExponent (final double magnitude,
            final double exponent) {
        myMagnitude = magnitude;
        myExponent = exponent;
    }

    @Override
    protected Force getCalculatedForce (final Mass target, final Assembly asm) {
        double distanceToCenter = getDistance(target, asm);
        double angleOfForce = getAngle(target, asm);
        // If exponent is negative, treat as a repulsion force by negating
        // angle
        if (getExponent() < 0) {
            angleOfForce *= -1;
        }
        // NOTE: Force magnitude calculated as follows:
        // (scalar * magnitude)
        // -----------------------------
        // (distanceToCenter)^(exponent)
        return new Force(angleOfForce, getScalar(target, asm) * getMagnitude()
                / Math.pow(distanceToCenter, Math.abs(getExponent())));
    }

    /**
     * Calculates distance to use in calculation.
     *
     * @param target Mass to find distance to
     * @param asm Assembly to use as context
     * @return Distance to use in calculation
     */
    protected abstract double getDistance (Mass target, Assembly asm);

    /**
     * Calculates angle to use for force.
     *
     * @param target Mass to find angle to
     * @param asm Assembly to use as context
     * @return Angle to use in calculation
     */
    protected abstract double getAngle (Mass target, Assembly asm);

    /**
     * Getter for magnitude.
     *
     * @return Magnitude of force
     */
    protected final double getMagnitude () {
        return myMagnitude;
    }

    /**
     * Getter for exponent.
     *
     * @return Exponent of force
     */
    protected final double getExponent () {
        return myExponent;
    }

    /**
     * Getter for scalar component of calculation.
     *
     * @param target target mass if needed
     * @param asm assembly that mass is part of
     * @return Scalar component of force
     */
    protected double getScalar (Mass target, Assembly asm) {
        return 1;
    }

}
